
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
using System.Xml;

using Swaf;
using Swaf.BizObj;
using Swaf.Container;
using Swaf.Security;

namespace Swaf.DataAccess.AdoNet
{
	public abstract class AdoNetDataAccess
	{
		public abstract object execute(INameAddressSupport info, out string sqql, out IDbCommand cmd);
		protected IApplication App { get { return Application.currentApp; } }
		protected virtual AdoNetFormatter Formatter { get { return null; } }

		protected bool needQuote(object fieldName, INameAddressSupport info)
		{
			bool need = true;
			if (Formatter != null && fieldName != null)
			{
				try
				{
					IBizObjField field = Formatter.BizObjType.getField(fieldName.ToString(), false);
					if (field != null &&
					  !(field.SampleValue is Int16 || field.SampleValue is Int32 || field.SampleValue is Int64 || field.SampleValue is decimal || field.SampleValue is float))
						need = false;
				}
				catch (Exception)
				{	}
			}
			return need;
		}
	}

	public class BizObjFieldDictionary : Dictionary<string, IBizObjField> {}

	public abstract class AdoNetFormatter
	{
		protected static BizObjFieldDictionary s_emptyFieldDic = new BizObjFieldDictionary();
		protected static NodeInfoDictionary s_emptyNodeDic = new NodeInfoDictionary();

		protected string m_bizObjType = "";
		protected IApplication App { get { return Application.currentApp; } }
		protected FieldInfoDictionary m_dataReaderFields = new FieldInfoDictionary();
		protected IComparable m_lastNodeKey = null;
		protected IBizObj m_currentBizObj;

		protected FieldInfo getField(string name)
		{
			FieldInfo field = null;
			m_dataReaderFields.TryGetValue(name.ToUpper(), out field);
			return field;
		}

		protected abstract string UpperName {get;}
		protected internal abstract IBizObjDefinition BizObjType { get;}
		protected virtual BizObjFieldDictionary ReaderColNameMappings { get { return s_emptyFieldDic; } }
		protected virtual NodeInfoDictionary ChildNodes { get { return s_emptyNodeDic; } }
		protected abstract bool RequiresParentName { get;}
		public abstract bool populateBizObj(IDataReader reader, bool parentKeyChanged, INameAddressSupport info);


		public IComparable LastNodeKey { get { return m_lastNodeKey; } set { m_lastNodeKey = value; } }
		public IBizObj CurrentBizObj { get { return m_currentBizObj; } set { m_currentBizObj = value; } }

		public ISecurityPrincipal CurrentPrincipal { get { return App.securityMgr.currentPrincipal; } }

		protected virtual void mapFieldsFromDataReader(DataTable readerStructure)
		{
			if (m_dataReaderFields.Count == 0)
			{
				foreach (DataRow col in readerStructure.Rows)
					mapField(col["ColumnName"].ToString(), (int)col["ColumnOrdinal"]);

				foreach (AdoNetFormatter child in ChildNodes.Values)
					child.mapFieldsFromDataReader(readerStructure);
			}
		}

		protected virtual void mapField(string colName, int colIndex)
		{
			try 
			{
				IBizObjField bizObjField = null;
				colName = colName.ToUpper();
				string realColName;
				if (colName.StartsWith(UpperName))
					realColName = colName.Substring(UpperName.Length);
				else
				{
					if (RequiresParentName)
						return;
					realColName = colName;
				}

				string fieldName = realColName.Replace('_','.').ToLower();
				if (!ReaderColNameMappings.TryGetValue(colName, out bizObjField))
					bizObjField = BizObjType.getField(fieldName, false);

				if (bizObjField != null)
				{
					m_dataReaderFields[realColName] = new FieldInfo(bizObjField, colIndex, fieldName);
					if (colName.Length != realColName.Length)
						m_dataReaderFields[colName] = m_dataReaderFields[realColName];
				}
			}
			catch(Exception e) 
			{
				//throw new Swaf.DataAccess.DataFormatException(GetType().Name, bizObjSpec, e);
			}
		}

		protected virtual void processFields(IDataReader reader)
		{
			foreach (FieldInfo field in m_dataReaderFields.Values)
			{
				object val = reader.GetValue(field.ReaderIndex);
				if(field.ConvertType && val != null && val != DBNull.Value)
				{
					if (field.FieldType.BizType == BizFieldType.BizObj || field.FieldType.BizType == BizFieldType.BizObjList)
					{
						if (!Cmn.IsEmpty(val))
						{
							try
							{
								XmlTextReader xml = new XmlTextReader(new StringReader(val.ToString()));
								xml.MoveToContent();
								if (field.FieldType.BizType == BizFieldType.BizObj)
									val = Swaf.BizObj.basic.FullBizObj.createInstance(null, ((IBizObj)field.FieldType.SampleValue).type, xml);
								else if (field.FieldType.BizType == BizFieldType.BizObjList)
									val = Swaf.BizObj.basic.FullBizObjList.createInstance(((IBizObjList)field.FieldType.SampleValue).objectType, null, xml);
							}
							catch (Exception e)
							{
								//Keep going.  The exception should be logged by now.  Only real exception that could happen
								//is that the bizobj/list could not be created because of a change to the structure of the
								//bizobj or something along those lines.
							}
						}
					}
					else
						val = BaseBizObj.BizObjFieldFactory.createFromNickName(field.FieldType.FactoryType, val);
				}
				if(val != null && val != System.DBNull.Value && m_currentBizObj != null)
					if (field.ExtraInfo == null)
						m_currentBizObj.put(field.FieldName, val);
					else
						m_currentBizObj.put(field.FieldName, val, field.ExtraInfo);
				string testName = field.FieldName;
				object val2 = m_currentBizObj.get(testName);
			}
		}

		protected virtual void process(IDataReader reader)
		{
		}
	}
	public class NodeInfoDictionary : Dictionary<string, AdoNetFormatter> { };

	public class FieldInfo
	{
		protected IBizObjField m_field;
		protected int m_readerIndex;
		protected bool m_convert = false;
		protected string m_extra = null;
		public int ReaderIndex { get { return m_readerIndex; } }
		public bool ConvertType { get { return m_convert; } set { m_convert = value; } }
		public string ExtraInfo { get { return m_extra; } set { m_extra = value; } }
		public string FieldName;

		public FieldInfo(IBizObjField field, int readerIndex, string fldName)
			:this(field, readerIndex)
		{
			FieldName = fldName;
		}

		public FieldInfo(IBizObjField field, int readerIndex)
		{
			m_field = field;
			m_readerIndex = readerIndex;
			if (field.SampleValue is DecimalFormattingNode || field.SampleValue is DateFormattingNode || string.Compare(field.FactoryType, "Guid", true) == 0 ||
				string.Compare(field.FactoryType,"Map", true) == 0 ||
				field.BizType == BizFieldType.BizObj || field.BizType == BizFieldType.BizObjList)
				m_convert = true;
		}

		public IBizObjField FieldType { get { return m_field; } }
	}
	public class FieldInfoDictionary : Dictionary<string, FieldInfo> {}
	public class FieldInfoList : List<FieldInfo> {};
}
